package server;

import capsules.CapsuleSpecification;
import capsules.CapsuleState;
import client.Identifier;
import client.Information;
import client.Operation;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.concurrent.atomic.AtomicBoolean;
import communication.Connection;
import communication.ConnectionException;
import constans.CapsuleConstans;
import constans.SizeConstans;
import java.util.logging.Level;
import java.util.logging.Logger;


class ThreadRecovery extends Thread{


	private Connection connection;
	private Authenticator authenticator;
	private Verifier verifier;
	private Recovery recovery;
	private Updater updater;	
	private File folderCapsuleTemporary;
	private Information information;
	private int numberCapsules;
	private boolean isLogin;
	private boolean isCorrect;
	private AtomicBoolean isStable;
    private Logger logger;



	public ThreadRecovery(Connection connection, Authenticator authenticator,Verifier verifier, Recovery recovery,
			Updater updater, File folderCapsuleTemporary, AtomicBoolean isStable, Logger logger) {
		this.connection = connection;
		this.authenticator = authenticator;
		this.verifier = verifier;
		this.recovery = recovery;
		this.updater = updater;
        this.isStable = isStable;
        this.logger = logger;
		this.numberCapsules = 0;
		this.folderCapsuleTemporary = folderCapsuleTemporary;
		this.isLogin = false;
		this.isCorrect = true;		
	}


    @Override
	public void run(){	

		try {


			Operation operation = (Operation) connection.receiveObject();		

			if(operation.equals(Operation.LOGIN)){
				doLogin();                
			} else{
				isCorrect = false;
			}


			if(isCorrect && isLogin){


				operation = (Operation) connection.receiveObject();

				if(operation.equals(Operation.VERIFY_STATE_SERVER)){
                    logger.log(Level.INFO,"Operation.VERIFY_STATE_SERVER("+information.getEmail()+")");
					connection.send(ServerState.RECOVERY);
                }
				else
					isCorrect = false;
			}

            while(isCorrect && isLogin){

				operation = (Operation) connection.receiveObject();

                switch(operation){
                    case SEND_OFFERED_SPACE:
                        logger.log(Level.INFO,"Operation.SEND_OFFERED_SPACE("+information.getEmail()+")");
                        doOfferedSpace();
                        break;
                    case VERIFY_STATE_CAPSULE:
                        logger.log(Level.INFO,"Operation.VERIFY_STATE_CAPSULE("+information.getEmail()+")");
                        doVerifyCapsuleState();
                        break;
                    case VERIFY_UPLOAD_CAPSULE:
                        logger.log(Level.INFO,"Operation.VERIFY_UPLOAD_CAPSULE("+information.getEmail()+")");
                        doVerifyCapsuleUpload();
                        break;
                    case UPLOAD_CAPSULE:
                        logger.log(Level.INFO,"Operation.UPLOAD_CAPSULE("+information.getEmail()+")");
                        doUploadCapsule();
                        break;
                    case LOGOUT:
                        logger.log(Level.INFO,"Operation.LOGOUT("+information.getEmail()+")");
                        doLogout();
                        isLogin= false;
                        break;
                    default:
                        isCorrect= false;
                        break;
                }
             }	
		}
		catch (ClassCastException e) {
             logger.log(Level.WARNING, e.getMessage());
        }
		catch (ConnectionException e) {
            logger.log(Level.WARNING, e.getMessage());
        }
		catch (FileNotFoundException e) {
             logger.log(Level.WARNING, e.getMessage());
        }
        catch (IOException e) {
             logger.log(Level.WARNING, e.getMessage());
        }
        catch (SQLException e) {
            isStable.set(false);
            logger.log(Level.SEVERE, e.getMessage());
		} finally{
			try {
				if(connection!=null){
					connection.close();
				}
			} catch (ConnectionException e) {}
		}


		if(isLogin){
            try {
                updater.setNumberCapsules(information.getEmail(), information.getId(), numberCapsules);
            } catch (SQLException e) {
                isStable.set(false);
                logger.log(Level.SEVERE, e.getMessage());
            }
    		doLogout();
        }
	}


	private void doUploadCapsule()
            throws FileNotFoundException, IOException,
            ConnectionException, SQLException{

        String nameCapsule = (String) connection.receiveString();
		
		File file = new File(folderCapsuleTemporary,  nameCapsule+information.getEmail()+information.getId()
                +CapsuleConstans.CAPSULE_TEMPORARY_EXTENSION);

		FileOutputStream fileOutput= null;
		try{

            
			fileOutput = new FileOutputStream(file, true);

            long offsetCapsule = file.length();
            connection.send(offsetCapsule);

            long lengthCapsule = recovery.getCapsuleLength(nameCapsule);

    		int numRead = 0;
			byte[] buffer = new byte[(int)SizeConstans.KByte];

			while (offsetCapsule < lengthCapsule) {

				numRead = connection.receiveBytes(buffer);

				fileOutput.write(buffer, 0, numRead);
                offsetCapsule+=numRead;
			}

            updater.setCapsuleTemporaryComplety(nameCapsule, information.getEmail(),information.getId());
			
			fileOutput.close();
       
		} catch (FileNotFoundException e) {
			try{
				if(fileOutput!=null){
					fileOutput.close();
					fileOutput = null;
				}
			}catch(IOException e1){}
			throw new FileNotFoundException();
		} catch (ConnectionException e) {
			try{
				if(fileOutput!=null){
					fileOutput.close();
					fileOutput = null;
				}
			}catch(IOException e1){}
        	throw new ConnectionException(e.getMessage());
		} catch (IOException e) {
			try{
				if(fileOutput!=null){
					fileOutput.close();
					fileOutput = null;
				}
			}catch(IOException e1){}
            throw new IOException(e);
		} catch (IndexOutOfBoundsException e) {
			try{
				if(fileOutput!=null){
					fileOutput.close();
					fileOutput = null;
				}
			}catch(IOException e1){}
			throw new ConnectionException();
		} 
	}


	private void  doVerifyCapsuleUpload()
            throws ConnectionException, SQLException {

		String nameCapsule = (String) connection.receiveString();

        if(nameCapsule!=null){

			boolean  isUpload =  recovery.getRecoveryCapsule(nameCapsule,information.getEmail(),information.getId());
         
            connection.send(isUpload);
		}
		else{
			isCorrect = false;
		}
	}

    private void doOfferedSpace()
            throws SQLException, ConnectionException,
            ClassCastException {

       long offeredSpace =  (long) connection.receiveLong();

       updater.setOfferedSpace(information.getEmail(), information.getId(), offeredSpace);
    }


    private void doVerifyCapsuleState()
            throws SQLException,
            ConnectionException,ClassCastException{

		CapsuleSpecification capsuleSpecification = (CapsuleSpecification) connection.receiveObject();

		if(capsuleSpecification!=null){

			CapsuleState capsuleState = verifier.getCapsuleState(capsuleSpecification);;

   		     if(capsuleState.equals(CapsuleState.CORRECT))
                numberCapsules++;

			connection.send(capsuleState);
		}
		else{
			isCorrect = false;
		}
	}

        
    private void doLogout(){
        authenticator.logoutClient(information.getEmail());
	}


	private void doLogin()
            throws ConnectionException, SQLException,
            IOException,ClassCastException{

		Identifier identifier = (Identifier) connection.receiveObject();

		if(identifier!=null && identifier.getSealedInfo()!=null && identifier.getSignedInfo()!=null){          

            if( (information = authenticator.loginClient(identifier))!=null){
                logger.log(Level.INFO,"Operation.LOGIN("+information.getEmail()+")");
                isLogin = true;
            }
            else{
                isLogin = false;
            }

            

			connection.send(isLogin);

		} else{
			isCorrect = false;
		}
	}


}

